/****************************************************************************************
*
*      SIMULATION DU FONCTIONNEMENT DE SICSTUS PROLOG EN UTILISANT JAVA & JASPER
*      -------------------------------------------------------------------------
*
*    A compiler et à exécuter en spécifiant le classpath de Jasper.
*
*      - sur Linux : /applis/sicstus-3.11.2/lib/sicstus-3.11.2/bin/jasper.jar
*
*          + executer la commande : export LD_LIBRARY_PATH=/applis/sicstus-3.11.2/lib/ 
*                à chaque session. 
*      
*      - sous Windows : y:\...
*
***************************************************************************************/

// Documentation : http://sicstus.sics.se/sicstus/docs/latest/html/jasper/

// importation des classes utiles à Jasper
import se.sics.jasper.*;

// pour les lectures au clavier
import java.io.*;

// pour utiliser les HashMap 
import java.util.*;

// pour les sockets
import java.net.*;

public class jSicstus {

	public static class Case {

		int x;
		int y;
		int j;

	}

	//	pour la connexion Socket 
	private String host = null;
	private int port = -999;
	private Socket socket = null;
	private BufferedReader inStream = null;	
  	private BufferedWriter outStream = null;



   	//	pour SICStus
	SICStus sp ;
	SPTerm plateauDep, depAdv, arrAdv, propAdv, blocAdv;
	SPTerm plateauArr, depJ, arrJ, propJ, blocJ;
	SPQuery query;

	String stringPlateau,stringCaseDep,stringCaseArr;
	ArrayList<Case> listePlateau;

	int joueur,joueurDef = 0,nouvellePartie = 1;


	public static ArrayList<Case> plateauSPtoList(SPTerm plateau){
		
		ArrayList<Case> al = new ArrayList<Case>();
		try{
			SPTerm[] spCases = plateau.toTermArray();

			for(int i=0;i<spCases.length;i++){
				SPTerm[] spCase = spCases[i].toTermArray();

				Case lcase = new Case();
				lcase.x = (int)spCase[0].getInteger();
				lcase.y = (int)spCase[1].getInteger();
				lcase.j = (int)spCase[2].getInteger();
				al.add(lcase);
			}

		}catch(Exception e){
			System.out.println("Exception : "+e);
		}

		return al;

	}

	public static SPTerm plateauListtoSP(SICStus sp, ArrayList<Case> plateau){
		
		SPTerm spCases = new SPTerm(sp);

		SPTerm spCase;

		for (Case lcase : plateau){
			try{
				spCases.consList(sp.readFromString("["+ lcase.x +","+ lcase.y +","+ lcase.j +"]."),spCases);
			}catch(Exception e){
				System.out.println("Exception : "+e);

			}
		}

		return spCases;
	
	}

	public static String parseCoup(SPTerm[] coup1, SPTerm[] coup2, SPTerm prop, SPTerm bloq){

		int c1x,c1y,c2x,c2y,ibloq,iprop;

		try 
		{
			c1x = (int)coup1[0].getInteger();
			c1y = (int)coup1[1].getInteger();
			
			c2x = (int)coup2[0].getInteger();
			c2y = (int)coup2[1].getInteger();

			ibloq = (int)bloq.getInteger();
			iprop = (int)prop.getInteger();

			return 	c1x +","+ c1y +","+ 
					c2x +","+ c2y +","+
					ibloq +","+ (iprop-1)+'\0';

		}catch ( Exception e )
		{
		  e.printStackTrace();
		}

		return "";

	}

	void tabStringToSPTerm(String l){


		String[] s = l.split(",");
		String stringCaseDep,stringCaseArr;

		stringCaseDep = "["+
		Integer.parseInt(s[0])+","+
		Integer.parseInt(s[1])+","+
		"2"+
		"].";

		stringCaseArr = "["+
		Integer.parseInt(s[2])+","+
		Integer.parseInt(s[3])+","+
		"2"+
		"].";

		try
		{
			depAdv = sp.readFromString(stringCaseDep);
			arrAdv = sp.readFromString(stringCaseArr);
			propAdv = new SPTerm(sp,Integer.parseInt(s[4])+1);
		}catch ( Exception e )
		{
		  e.printStackTrace();
		}
		System.out.println(depAdv+" "+arrAdv);
	}


	public void setUp()
	{
		System.out.println("JavaToC.setUp() invoked");

		try
		{
			//	connexion à la socket C
			socket = new Socket(host, port);
			inStream = new BufferedReader(new InputStreamReader(
				socket.getInputStream()));
			outStream = new BufferedWriter(new OutputStreamWriter(
				socket.getOutputStream()));
			//	chargement du fichier prolog
			sp = new SICStus();
			sp.load("../ia/ia.pl");
		}
		catch(UnknownHostException e)
		{
			System.err.println("Cannot find host called: " + host);
			e.printStackTrace();
			System.exit(-1);
		}
		catch(IOException e)
		{
			System.err.println("Could not establish connection for " + host);
			e.printStackTrace();
			System.exit(-1);
		}
		catch ( Exception e )
		{
			System.err.println("Prolog error ");
			e.printStackTrace();
			System.exit(-1);
		}
	}
	
	public void converse()
	{
		System.out.println("JavaToC.converse() invoked");

		if(socket == null){
			System.out.println("socket null !");
			return;
		}
		String l;
		String s_coup;
		try
		{

			if(joueurDef == 0){

			// 1ere reception : le joueur !
				if(((l = inStream.readLine()) == null) || l.charAt(0) == 'n' || l.charAt(0) == 'o'){
					System.out.println("first inStream null ! : "+l);
					nouvellePartie = 0;
					return;
				}

				System.out.println("lecture du flux d'entrée");

			// lecture du premier envoi du joueur
				System.out.println(l);


			
				joueurDef = 1;
				if (l.charAt(0) == '0'){
					joueur = 0;
					stringPlateau = "[[0,8,1],[4,0,1],[8,4,1],[8,0,2],[0,4,2],[4,8,2]].";
				}else{
					joueur = 1;
					stringPlateau = "[[0,8,2],[4,0,2],[8,4,2],[8,0,1],[0,4,1],[4,8,1]].";
				
				}
			}

			// 

			if(((l = inStream.readLine()) == null) || l.charAt(0) == 'n' || l.charAt(0) == 'o'){
				System.out.println("first inStream null ! : "+l);
				nouvellePartie = 0;
				return;
			}



			plateauArr = new SPTerm(sp).putVariable();
			depJ = new SPTerm(sp).putVariable();
			arrJ = new SPTerm(sp).putVariable();
			propJ = new SPTerm(sp).putVariable();

			//	s'il s'agit du caractere 'a', ceci signifie que je commence
			if(l.charAt(0) == 'a'){
				System.out.println("je commence");
				
				// appel au prédicat "jouer"
					
				plateauDep = sp.readFromString(stringPlateau);

				query = sp.openQuery(
					"user", 
					"jouer",
					new SPTerm[] { plateauDep, plateauArr, depJ, arrJ, propJ });

				query.nextSolution();
				System.out.println("reponse init \n"+plateauArr+"\n"+depJ+"\n"+arrJ+"\n"+propJ+"\n"+blocJ+"\n");

				// transforme mon coup prolog en String
				s_coup = parseCoup(depJ.toTermArray(),arrJ.toTermArray(), propJ, sp.readFromString("0."));

			}else{
				System.out.println("l'adversaire commence");
				// il s'agit du 1er coup adverse, j'initialise mon plateau 
				//	et sauvegarde le plateau retour pour les coups futurs
				
				plateauDep = sp.readFromString(stringPlateau);

				// convertis la chaine reçu du joueur en objet Prolog 
				tabStringToSPTerm(l);

				blocJ = new SPTerm(sp).putVariable();
				plateauArr = new SPTerm(sp).putVariable();
			
				System.out.println("1ere Question ? : \n"+plateauDep+"\n"+depAdv+"\n"+arrAdv+"\n"+propAdv+"\n\n");

				// appel au prédicat "jouer"
				query = sp.openQuery(
					"user", 
					"jouer",
					new SPTerm[] {	plateauDep,	depAdv,	arrAdv,	propAdv,
						plateauArr,	depJ,	arrJ,	propJ,	blocJ });

				query.nextSolution();
				System.out.println("1ere reponse \n"+plateauArr+"\n"+depJ+"\n"+arrJ+"\n"+propJ+"\n"+blocJ+"\n");

				// transforme mon coup prolog en String
				s_coup = parseCoup(depJ.toTermArray(),arrJ.toTermArray(), propJ, blocJ );

			}

			System.out.println("mon coup est : "+s_coup);

			// envoi du coup à jouer "chainé" au joueur 
			outStream.write(s_coup);
			outStream.flush();


			// je sauvegarde mon plateau car le query.close réinitialise tout les SPTerm
			listePlateau = plateauSPtoList(plateauArr);
			query.close();


			// 	la partie continue tant que le flux ne s'arrête pas
			//	( à modifier pour prendre en compte le recommencement d'une partie )
			while(((l = inStream.readLine()) != null) && l.charAt(0) != 'n' && l.charAt(0) != 'o'){

				System.out.println(l.charAt(0));

				plateauDep = new SPTerm(sp,plateauListtoSP(sp,listePlateau));

				// convertis la chaine reçu du joueur en objet Prolog 
				tabStringToSPTerm(l);

				plateauArr = new SPTerm(sp).putVariable();
				depJ = new SPTerm(sp).putVariable();
				arrJ = new SPTerm(sp).putVariable();
				propJ = new SPTerm(sp).putVariable();
				blocJ = new SPTerm(sp).putVariable();

				// appel au prédicat "jouer"
				query = sp.openQuery(
					"user", 
					"jouer",
					new SPTerm[] {	plateauDep,	depAdv,	arrAdv,	propAdv,
						plateauArr,	depJ,	arrJ,	propJ,	blocJ });

				System.out.println("question ? \n"+plateauDep+"\n"+depAdv+"\n"+arrAdv+"\n"+propAdv+"\n\n");


				// Si prolog ne renvoie pas de solution (erreur)
				if (query.nextSolution() == false){
					System.out.println("Aucune solution");
					break;
				}

				System.out.println("reponse \n"+plateauArr+"\n"+depJ+"\n"+arrJ+"\n"+propJ+"\n"+blocJ+"\n");

				s_coup = parseCoup(depJ.toTermArray(),arrJ.toTermArray(), propJ, blocJ );

				System.out.println("coup de la reponse : "+s_coup+"\n");

				// envoi du coup à jouer "chainé" au joueur 
				outStream.write(s_coup);
				outStream.flush();

				listePlateau = plateauSPtoList(plateauArr);
				query.close();

			}

			if (l == null || l.charAt(0) == 'n'){
				nouvellePartie = 0;
			}
		}
		catch(IOException e)
		{
			System.err.println("Conversation error with host " + host);
			e.printStackTrace();
		}
		catch ( Exception e )
		{
			e.printStackTrace();
		}
	}
	
	public void cleanUp()
	{
		try
		{
			if (inStream != null)
				inStream.close();
			if (socket != null)
				socket.close();
		}
		catch(IOException e)
		{
			System.err.println("Error in cleanup");
			e.printStackTrace();
			System.exit(-1);
		}
	}

	public static void main(String[] args) {

		jSicstus caller = new jSicstus();
		caller.host = "localhost";
		caller.port = new Integer(args[0]).intValue();
		
		caller.setUp();

		while(caller.nouvellePartie == 1){
			caller.converse();
		}

		caller.cleanUp();

	}

}
